﻿
/*============== simpleIRC - http://matthias.van-eeghem.com/ ==============*
 *                                                                         *
 *  File: Channel.cs                                                       *
 *  Project: IRC.dll                                                       *
 *  Author(s): Matthias Van Eeghem <matthias@van-eeghem.com>               *
 *                                                                         *
 * ========================================================================*
 *                                                                         *
 *  This program is free software: you can redistribute it and/or modify   *
 *  it under the terms of the GNU General Public License as published by   *
 *  the Free Software Foundation, either version 3 of the License, or      *
 *  (at your option) any later version.                                    *
 *                                                                         *
 *  This program is distributed in the hope that it will be useful,        *
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of         *
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
 *  GNU General Public License for more details.                           *
 *                                                                         *
 *  You should have received a copy of the GNU General Public License      *
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.  *
 *                                                                         *
===========================================================================*/

namespace simpleIRC
{
    /// <summary>
    /// This class contains all the Channel related core functions.
    /// </summary>
    
    public class Channel
    {
        // This property will hold the reference to the Server object
        // The server object stored is the server this channel is on.
        public Server Server { get; private set; }
        public Logger Logger { get; private set; }

        // Holds the topic for each channel
        public string Topic { get; set; }
        // If this variable is set to false, the Topic set by command has not been sent yet by the server, otherwise, it has.
        public bool TopicSetBySent { get; set; }
        // Holds a string containing a space seperated userlist
        public string UserList { get; set; }
        // Holds all text that was said in a channel window.
        public string Text { get; set; }
        // Holds the name of the channel
        public string Name { get; set; }
        // Holds the password of the channel, empty string if there's none
        public string Password { get; set; }
        // This variable holds the Channel.Index for this Channel
        public int Index { get; set; }

        /// <summary>
        /// The constructor of the Channel class, this sets the class variables.
        /// </summary>
        /// <param name="serverIndex">The index of the server the channel is on</param>
        /// <param name="channelName">The channel name</param>
        /// <param name="channelPassword">The password for that channel, empty string if there is none</param>

        public Channel(int serverIndex, string channelName, string channelPassword)
        {
            // Initalise all the properties
            Topic = null;
            TopicSetBySent = false;
            UserList = "";
            Text = "";

            // Save the channel name and password
            Name = channelName;
            Password = channelPassword;
            Index = Core.ChannelList.Count;

            // Get the server object that is the channels parent and save it
            Server = Core.ServerList[serverIndex];

            // Add the channel object to the ChannelList
            Core.ChannelList.Add(this);
            Core.LoggerList.Add(new Logger(Server.Hostname.ToLower(), channelName));

            Logger = Core.LoggerList[(Core.LoggerList.Count - 1)];
        }

        /// <summary>
        /// This method adds a new client name to the userlist
        /// </summary>
        /// <param name="nickName">The name of the user</param>

        public void AddUser(string nickName)
        {
            // Add the username to the list
            UserList += nickName + " ";
            // Replace a double space by one, just to make sure
            UserList = UserList.Replace("  ", " ");
        }

        /// <summary>
        /// This method strips all rights from a username
        /// </summary>
        /// <param name="Nickname">The nickname</param>
        /// <returns>The name without all the userlevels</returns>

        public string GetNameWithoutRights(string nickname)
        {
            return nickname.Replace("~", "").Replace("&", "").Replace("@", "").Replace("%", "").Replace("+", "");
        }

        /// <summary>
        /// This method returns a user's level
        /// </summary>
        /// <param name="nickname">The nickname to get the level from</param>
        /// <returns>The character containing the userlevel</returns>

        public string GetUserHighestRights(string nickname)
        {
            // We're adding a space after the name because we don't want "Bla" and "Blabla" to return the same userlevel
            // With this "fix", "Bla " and "Blabla " will return other things.
            nickname = nickname + " ";

            // Get the index of the username on the channel list.
            int PosIndex = UserList.IndexOf(nickname);

            if (PosIndex != -1)
            {
                // The NAMESX protocol is supported
                if (Server.NamesXSupported)
                {
                    // Get the string starting from the first index up until the index found
                    string TempString = UserList.Substring(0, PosIndex);

                    // Get the position of the last space in the string
                    int SpaceIndex = TempString.LastIndexOf(' ');

                    // Get the "highest" level from the username
                    char TempLevel = UserList[SpaceIndex + 1];

                    if (TempLevel == '~' || TempLevel == '&' || TempLevel == '@' || TempLevel == '%' || TempLevel == '+')
                    {
                        return TempLevel.ToString();
                    }
                }

                // The server doesn't support NAMESX
                else
                {
                    char TempLevel = UserList[PosIndex];

                    // If we have a valid level, return it
                    if (TempLevel == '~' || TempLevel == '&' || TempLevel == '@' || TempLevel == '%' || TempLevel == '+')
                    {
                        return UserList[PosIndex].ToString();
                    }
                }
            }
            return "";
        }

        /// <summary>
        /// This method removes a user from the userlist
        /// </summary>
        /// <param name="nickname">The username to remove</param>

        public void RemoveUser(string nickname)
        {
            // Find the index of the user name in the channel list.
            int PosIndex = UserList.IndexOf(nickname);

            int StartIndex = 0;
            int RemoveChars = 0;

            if (PosIndex != -1)
            {
                // NamesX is supported
                if (Server.NamesXSupported)
                {
                    // Get the string starting from the first index up until the index found
                    string TempString = UserList.Substring(0, PosIndex);

                    // Get the position of the last space in the string
                    int FirstLevelPos = TempString.LastIndexOf(' ');

                    // Check if we have a valid index
                    if (FirstLevelPos != -1)
                    {
                        // The amount of characters we have to remove, this is the amount of userlevels + the nickname length
                        int AmountOfCharsToRemove = (PosIndex - FirstLevelPos + nickname.Length);

                        // We didn't remove the space, so replace all double spaces with one
                        UserList = UserList.Remove(FirstLevelPos, AmountOfCharsToRemove).Replace("  ", " ");
                    }

                    // If no space is found, this means the user is the first user in the string
                    else
                    {
                        UserList = UserList.Remove(0, UserList.IndexOf(' '));
                    }
                }
                else
                {
                    // If the found index is 0, the user doesn't have any rights and the user is at the start of the string.
                    if (PosIndex == 0)
                    {
                        // Just set it to zero.
                        StartIndex = 0;

                        // The amount of characters to remove is equal to the length of that user's name.
                        RemoveChars = nickname.Length;
                    }

                    // If the found index is 1, the user is at the start of the string and has rights.
                    else if (PosIndex == 1)
                    {
                        // Set it to zero.
                        StartIndex = 0;

                        // The removechars should be incremented too.
                        RemoveChars = (nickname.Length + 1);
                    }

                    // The found index is not 0 or 1, we need to do some more checking.
                    else
                    {
                        // The start index is -1 for the rights character and -1 for the space.
                        StartIndex = (PosIndex - 1);

                        // If we get rid of 2 chars at the start, we need to increase the RemoveChars with 2 as well.
                        RemoveChars = (nickname.Length + 1);
                    }

                    // Now remove that string from the channel user list.
                    UserList = UserList.Remove(StartIndex, RemoveChars).Replace("  ", " ");
                }
            }
        }

        /// <summary>
        /// This method gives a user channel rights
        /// </summary>
        /// <param name="nickname">The nickname to give the rights to</param>
        /// <param name="userRights">The rights to give</param>

        public void GiveUserRights(string nickname, char userRights)
        {
            // Find the index of the user name in the channel list.
            int PosIndex = UserList.IndexOf(nickname);

            if (PosIndex != -1)
            {
                // Get the string starting from the first index up until the index found
                string TempString = UserList.Substring(0, PosIndex);

                // Get the position of the last space in the string
                int FirstLevelPos = TempString.LastIndexOf(' ') + 1;

                string UserRights = UserList.Substring(FirstLevelPos, (PosIndex - FirstLevelPos)) + GetLevelFromCharacter(userRights);

                // We're initialising a new string, so the rights are in proper order.
                string NewRights = "";

                if (UserRights.IndexOf('~') != -1) NewRights += "~";
                if (UserRights.IndexOf('&') != -1) NewRights += "&";
                if (UserRights.IndexOf('@') != -1) NewRights += "@";
                if (UserRights.IndexOf('%') != -1) NewRights += "%";
                if (UserRights.IndexOf('+') != -1) NewRights += "+";

                // Remove the old rights list, and insert the new string
                UserList = UserList.Remove(FirstLevelPos, (PosIndex - FirstLevelPos));
                UserList = UserList.Insert(FirstLevelPos, NewRights);
            }
        }

        /// <summary>
        /// This method removes a user's channel rights
        /// </summary>
        /// <param name="nickname">The nickname to take the rights from</param>
        /// <param name="userRights">The rights to take</param>

        public void RemoveUserRights(string nickname, char userRights)
        {
            // Find the index of the user name in the channel list.
            int PosIndex = UserList.IndexOf(nickname);

            if (PosIndex != -1)
            {
                // Get the string starting from the first index up until the index found
                string TempString = UserList.Substring(0, PosIndex);

                // Get the position of the last space in the string
                int FirstLevelPos = TempString.LastIndexOf(' ') + 1;

                // Get the users rights, and replace the char to remove with ""
                string UserRights = UserList.Substring(FirstLevelPos, (PosIndex - FirstLevelPos));
                UserRights = UserRights.Replace(GetLevelFromCharacter(userRights).ToString(), "");

                // Remove the old rights list, and insert the new string
                UserList = UserList.Remove(FirstLevelPos, (PosIndex - FirstLevelPos));
                UserList = UserList.Insert(FirstLevelPos, UserRights);
            }
        }

        /// <summary>
        /// This method converts a letter character to a symbol character
        /// </summary>
        /// <param name="character">The character to convert</param>
        /// <returns>The converted symbol</returns>

        private char GetLevelFromCharacter(char character)
        {
            switch(character)
            {
                case 'v': return '+';
                case 'h': return '%';
                case 'o': return '@';
                case 'a': return '&';
                case 'q': return '~';
            }

            return ' ';
        }

        /// <summary>
        /// This function joins the specified channel.
        /// </summary>
        /// <param name="ChannelPassword">This optional string specifies the password to join with</param>

        public void Join()
        {
            // He hasn't, join the channel.
            Server.SendData("JOIN " + Name + " " + Password);

            // Send the TOPIC command, to request the topic of the channel.
            Server.SendData("TOPIC " + Name);
        }

        /// <summary>
        /// This method parts the user from a specific channel.
        /// </summary>
        /// <param name="optionalReason">The optional reason</param>
        /// <param name="sendPart">If this bool is set to true, this function also sends the PART command to the server</param>

        public void Part(string optionalReason = "", bool sendPart = true)
        {
            // Send the PART command
            if(sendPart) Server.SendData("PART " + Name + " " + optionalReason);

            Logger.Remove();
            Core.ChannelList[Index] = null;
        }
    }
}
